package master

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sync"

	"go-gateworker/internal/config"
)

type Master struct {
	cfg      *config.Config
	workers  []*Worker
	stopChan chan struct{}
	wg       sync.WaitGroup
}

type Worker struct {
	ID      int
	Port    int
	Process *os.Process
	Cmd     *exec.Cmd
}

func New(cfg *config.Config) *Master {
	return &Master{
		cfg:      cfg,
		stopChan: make(chan struct{}),
	}
}

func (m *Master) Start() error {
	// 获取当前可执行文件路径
	exePath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %v", err)
	}

	// 启动 worker 进程
	for i := 0; i < m.cfg.NumWorkers; i++ {
		port := m.cfg.WorkerPortStart + i
		worker := &Worker{
			ID:   i,
			Port: port,
		}

		cmd := exec.Command(exePath,
			"-mode", "worker",
			"-config", "config.json",
			"-worker-id", fmt.Sprintf("%d", i),
			"-worker-port", fmt.Sprintf("%d", port),
		)

		// 设置工作目录
		cmd.Dir = filepath.Dir(exePath)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Start(); err != nil {
			log.Printf("Failed to start worker %d: %v", i, err)
			continue
		}

		worker.Process = cmd.Process
		worker.Cmd = cmd
		m.workers = append(m.workers, worker)

		// 监控 worker 进程
		m.wg.Add(1)
		go m.monitorWorker(worker)
	}

	// 等待停止信号
	<-m.stopChan
	return nil
}

func (m *Master) Stop() {
	close(m.stopChan)

	// 停止所有 worker
	for _, worker := range m.workers {
		if worker.Process != nil {
			worker.Process.Kill()
		}
	}

	m.wg.Wait()
}

func (m *Master) monitorWorker(worker *Worker) {
	defer m.wg.Done()

	// 等待进程结束
	if err := worker.Cmd.Wait(); err != nil {
		log.Printf("Worker %d exited with error: %v", worker.ID, err)
	}

	// 如果收到停止信号，不要重启
	select {
	case <-m.stopChan:
		return
	default:
	}

	// 重启 worker
	log.Printf("Restarting worker %d", worker.ID)
	m.restartWorker(worker)
}

func (m *Master) restartWorker(worker *Worker) {
	exePath, err := os.Executable()
	if err != nil {
		log.Printf("Failed to get executable path: %v", err)
		return
	}

	cmd := exec.Command(exePath,
		"-mode", "worker",
		"-config", "config.json",
		"-worker-id", fmt.Sprintf("%d", worker.ID),
		"-worker-port", fmt.Sprintf("%d", worker.Port),
	)

	cmd.Dir = filepath.Dir(exePath)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Start(); err != nil {
		log.Printf("Failed to restart worker %d: %v", worker.ID, err)
		return
	}

	worker.Process = cmd.Process
	worker.Cmd = cmd

	// 继续监控新的进程
	m.wg.Add(1)
	go m.monitorWorker(worker)
}
